/*        
	Copyright (C) 2010- Peer internet solutions

	Copyright (C) 2013 Pontificia Universidad Catolica del Peru
	contact: http://avatar.inf.pucp.edu.pe/realidad-aumentada/
	address contact: Av. Universitaria 1801, San Miguel, Lima-Peru.

	This file is part of UbicAR PUCP.
	
	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with This program.  If not, see <http://www.gnu.org/licenses/>.
*/
/*
	Modified by: Pedro Carrión Castagnola
	Pontificia Universidad Catolica del Peru
	Direccion de Informatica Academica

	Modified by: Alvaro Roger Agüero Sánchez, Rodolfo Javier Galvez Meza
	Pontificia Universidad Catolica del Peru
	Departamento de Ingeniería
*/

package org.grupoavatar.guiaubicua.mix;

import java.text.DecimalFormat;
import java.util.ArrayList;

import org.grupoavatar.guiaubicua.R;
import org.grupoavatar.guiaubicua.VAR;
import org.grupoavatar.guiaubicua.VDownloading;
import org.grupoavatar.guiaubicua.VInicial;
import org.grupoavatar.guiaubicua.mix.data.DataSource;
import org.grupoavatar.guiaubicua.mix.gui.PaintScreen;
import org.grupoavatar.guiaubicua.mix.gui.ScreenLine;
import org.grupoavatar.guiaubicua.mix.gui.TextObj;
import org.grupoavatar.guiaubicua.mix.reality.PhysicalPlace;
import org.grupoavatar.guiaubicua.mix.render.Camera;
import org.grupoavatar.guiaubicua.mix.render.MixVector;
import org.grupoavatar.guiaubicua.auxiliares.Item;
import org.grupoavatar.guiaubicua.auxiliares.Servicio;
import org.grupoavatar.guiaubicua.auxiliares.Utiles;
import org.grupoavatar.guiaubicua.mix.Marker;
import org.grupoavatar.guiaubicua.entidades.ARMarker;
import org.grupoavatar.guiaubicua.entities.POIBean;
import org.grupoavatar.guiaubicua.gestores.GestorConfiguracion;
import org.grupoavatar.guiaubicua.controller.CategoriaController;
import org.grupoavatar.guiaubicua.controller.POIController;
import org.grupoavatar.guiaubicua.dialogos.POI_Locked_Dialog;
import org.grupoavatar.guiaubicua.dialogos.POI_Locked_Points;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.location.Location;

/*abstract*/ public class Marker implements Comparable<Marker> {

	private String ID;
	protected String title;
	private POIBean poi;
	private boolean underline = false;
	protected PhysicalPlace mGeoLoc;
	// distance from user to mGeoLoc in meters
	protected double distance;

	private boolean active;
	protected boolean unicoMarker;

	// Draw properties
	protected boolean isVisible;
	protected boolean isLookingAt;

//	private float deltaCenter;
	public MixVector cMarker = new MixVector();
	protected MixVector signMarker = new MixVector();
	
	protected MixVector locationVector = new MixVector();
	private MixVector origin = new MixVector(0, 0, 0);
	private MixVector upV = new MixVector(0, 1, 0);
	private ScreenLine pPt = new ScreenLine();

	protected Label txtLab = new Label();
	protected TextObj textBlock;
	protected double radius;
	
	public float addY;
	public boolean alturaEstablecida = false;
	//DEFINICION DE ATRIBUTO QUE DETERMINARA SI EL PUNTO DEBE PINTARSE COMO CERCANO
	public boolean obstruido=true;
	//DEFINICION DE ATRIBUTO QUE DETERMINARA LOS PUNTOS QUE SON OBSTRUIDOS
	private ArrayList<Marker> obstruidos = new ArrayList<Marker>(); 
	//DEFINICION DE ATRIBUTO QUE ALMACENA LAS ACCIONES POR PUNTO
	private ArrayList<Item> acciones;
	
	private int visibilidadMinima;
	private double distanciaMayorTransparencia; //en metros
	private double distanciaSinTransparencia; //en metros
	private float pendiente;
	
	public Marker(POIBean poi/*String title, double latitude, double longitude, double altitude,ArrayList<String> enlaces, DataSource.DATASOURCE datasource, ArrayList<Servicio> servicios*/) {
		super();
		this.poi = poi;
		this.active = false;
		this.title = poi.getTitle();
		this.mGeoLoc = new PhysicalPlace(poi.getLatitud(), poi.getLongitud(), poi.getAltitud());
		
//		this.ID=datasource+"##"+title; //mGeoLoc.toString();
		
		this.alturaEstablecida = false;
		
		//visibilidad
		//a distanciaMayorTransparencia o más, la visibilidad será de porcVisibilidad porciento
		distanciaMayorTransparencia = 700; 
		//a distanciaSinTransparencia o menos, la visibilidad será de 100%
		distanciaSinTransparencia = 100; 
		float porcVisibilidad = 0.65f;
		visibilidadMinima = (int) Math.round(porcVisibilidad*255);
		pendiente = (float) ((visibilidadMinima - 255) / (distanciaMayorTransparencia - distanciaSinTransparencia));
	}
	
	public POIBean getPOI() {
		return poi;
	}
	
	public String getTitle(){
		return title;
	}
	
	public int getIcon()	{
//		if (((ARMarker)this).tieneIcono()) 
//			return VInicial.getContext().getDataSourceCircleIconRA(((ARMarker)this).getIcono());
//		else
//			return MixContext.getDataSourceIcon(datasource);
		return poi.getIcono();
	}
	
	public int getCircleIcon()	{
//		if (((ARMarker)this).tieneIcono()) {
//			return VInicial.getContext().getDataSourceCircleIcon(((ARMarker)this).getIcono());
//		} else {
//			return VInicial.getContext().getDataSourceCircleIcon(datasource);
//		}
		return poi.getIcono();
	}

//	public ArrayList<String> getURL(){
//		return URL;
//	}

	public double getLatitude() {
		return mGeoLoc.getLatitude();
	}
	
	public double getLongitude() {
		return mGeoLoc.getLongitude();
	}
	
	public double getAltitude() {
		return mGeoLoc.getAltitude();
	}
	
	public MixVector getLocationVector() {
		return locationVector;
	}
	
//	public ArrayList<Servicio> getServicios(){
//		return servicios;
//	}
	
//	public DataSource.DATASOURCE getDatasource() {
//		return datasource;
//	}

//	public void setDatasource(DataSource.DATASOURCE datasource) {
//		this.datasource = datasource;
//	}

	private void cCMarker(MixVector originalPoint, Camera viewCam, float addX, float addY) {

		// Temp properties
		MixVector tmpa = new MixVector(originalPoint);
		MixVector tmpc = new MixVector(upV);
		tmpa.add(locationVector); //3 //locationVector
		tmpc.add(locationVector); //3
		tmpa.sub(viewCam.lco); //4 //locationVector
		tmpc.sub(viewCam.lco); //4
		tmpa.prod(viewCam.transform); //5 //locationVector x transform //transform = RM (rotation matrix)
		tmpc.prod(viewCam.transform); //5

		MixVector tmpb = new MixVector();
		viewCam.projectPoint(tmpa, tmpb, addX, addY); //6
		cMarker.set(tmpb); //7
		viewCam.projectPoint(tmpc, tmpb, addX, addY); //6
		signMarker.set(tmpb); //7
	}

	private void calcV(Camera viewCam) {
		isVisible = false;
		//isLooking AT me dice si esta enfocando al punto, no borrar, 
		//puede utilizarce en un futuro...
//		isLookingAt = false;
//		deltaCenter = Float.MAX_VALUE;

		if (cMarker.z < -1f) {
//			isVisible = true;

//			if (MixUtils.pointInside(cMarker.x, cMarker.y, 0, 0,
//					viewCam.width, viewCam.height)) {
			float extra = viewCam.height / 25f * 7f;
			if (MixUtils.pointInsideX(cMarker.x, 0 - extra,viewCam.width + extra * 2)) {
				isVisible = true;
//				float xDist = cMarker.x - viewCam.width / 2;
//				float yDist = cMarker.y - viewCam.height / 2;
//				float dist = xDist * xDist + yDist * yDist;
//
//				deltaCenter = (float) Math.sqrt(dist);
//
//				if (dist < 50 * 50) {
//					isLookingAt = true;
//				}
			}
		}
	}

	public void update(Location curGPSFix) {
//		// An elevation of 0.0 probably means that the elevation of the
//		// POI is not known and should be set to the users GPS height
//		// Note: this could be improved with calls to 
//		// http://www.geonames.org/export/web-services.html#astergdem 
//		// to estimate the correct height with DEM models like SRTM, AGDEM or GTOPO30
//		if(mGeoLoc.getAltitude()==0.0)
//			mGeoLoc.setAltitude(curGPSFix.getAltitude());
		 
		// compute the relative position vector from user position to POI location
		PhysicalPlace.convLocToVec(curGPSFix, mGeoLoc, locationVector);
	}

	public void calcPaint(Camera viewCam, float addX, float addY) {
		cCMarker(origin, viewCam, addX, addY);
		calcV(viewCam);
	}

	private boolean isClickValid(float x, float y) {		
		float currentAngle = MixUtils.getAngle(cMarker.x, cMarker.y,
		signMarker.x, signMarker.y);
		float probError = 1.5f;
		pPt.x = x - cMarker.x;
		pPt.y = y - cMarker.y;
		pPt.rotate(Math.toRadians(-(currentAngle + 90)));
		
		pPt.x = Math.abs(pPt.x);
		pPt.y = Math.abs(pPt.y);
//		distX = Math.abs(distX);
//		distY = Math.abs(distY);
//		Log.d(VInicial.TAG, "-----------");
//		Log.d(VInicial.TAG, "Click en: " + x + ", " + y);
//		Log.d(VInicial.TAG, "POI en: " + signMarker.x + ", " + signMarker.y);
//		Log.d(VInicial.TAG, "-----------");
//		Log.d(VInicial.TAG, "PPT: " + pPt.x + ", " + pPt.y);
//		Log.d(VInicial.TAG, "Dist: " + distX + ", " + distY);
//		Log.d(VInicial.TAG, "radius: " + radius);
//		Log.d(VInicial.TAG, "-----------");
		if ((pPt.x <= radius*probError) && (pPt.y <= radius*probError)){
			return true;
		} else {
			return false;
		}
	}
	
	public void draw(PaintScreen dw) {
		if (!obstruido) {		
			dw.setColor(poi.getCategoria().getColor());
			drawCircle(dw);
			drawTextBlock(dw);		
		} 
		else {
			if (this.isUnicoMarker()){
				dw.setColor(poi.getCategoria().getColor());
				drawCircle(dw);
				drawTextBlock(dw);
			}
		}	
	}

	public void drawCircle(PaintScreen dw) {
		if (isVisible) {
			float maxHeight = dw.getHeight();
			//inicio nuevo para resize
//			double angle = 2.0*Math.atan2(10,distance);
//			double radius2 = Math.max(Math.min(angle/0.44 * maxHeight, maxHeight),maxHeight/25f);
			//fin nuevo para resize
			if (unicoMarker)	{
				dw.setStrokeWidth(maxHeight / 100f);
				dw.setFill(false);
				radius = Math.max( ((maxHeight/2 - maxHeight/25f)/400*(400 - distance) + maxHeight/25f), maxHeight/25f);
				dw.paintCircle(cMarker.x, cMarker.y, (float)radius);
			}
			else	{
				radius = maxHeight/20f;
				Bitmap bitmap = null;
				float x=cMarker.x - (float)radius;
				float y=cMarker.y - (float)radius;
				if (poi.tieneIcono()) {
//					bitmap = DataSource.getBitmap(icono, (int)(radius*2), icono==2);
					bitmap = getImgWithSize(POIController.getInstance().getIconoDrawable(VInicial.getContext(), poi), (int)(radius*2));
//					if (icono==2) x = cMarker.x - (float)0.5*bitmap.getWidth();
				} else {
					bitmap = getImgWithSize(CategoriaController.getInstance().getIconoDrawable(VInicial.getContext(), poi), (int)(radius*2));
				}				
				if(bitmap!=null) {										
					dw.paintBitmap(bitmap, x, y);	
				}
				else	{
					dw.setStrokeWidth(maxHeight / 100f);
					dw.setFill(true);
					dw.paintCircle(cMarker.x, cMarker.y, (float)radius);
				}
			}
			
		}
	}
	
	public void drawTextBlock(PaintScreen dw) {
		if (isVisible) {
			float maxHeight = Math.round(dw.getHeight() / 10f) + 1;
			
			String textStr = "";

			double d = distance;
			DecimalFormat df = new DecimalFormat("@#");
			if (d < 1000.0) {
				textStr = title + " (" + df.format(d) + "m)";
			} else {
				d = d / 1000.0;
				textStr = title + " (" + df.format(d) + "km)";
			}
			
			int transp = Math.round(getVisibilidadSegunDistancia());
			int bgColor = Color.argb((int) (Math.round(transp / 1.5)), 200,
					200, 200);
			int textColor = Color.argb(transp, 10, 10, 10);
			float fontSize = Math.round(maxHeight / 2.5f);
			if (fontSize > 18f) fontSize = 18f;
			textBlock = new TextObj(textStr, fontSize, 100,
					bgColor, bgColor, textColor, Color.argb(64, 0, 0, 0),
					dw.getTextAsc() / 2 , dw, false);

			float currentAngle = MixUtils.getAngle(cMarker.x, cMarker.y,
					signMarker.x, signMarker.y);

			this.txtLab.prepare(textBlock);

			dw.setStrokeWidth(1f);
			dw.setFill(true);
			if (unicoMarker)	{
				dw.paintObj(txtLab, signMarker.x - txtLab.getWidth()/2, signMarker.y + maxHeight, currentAngle + 90, 1, false);
			}
			else	{
				dw.paintObj(txtLab, signMarker.x - txtLab.getWidth()/2, cMarker.y + maxHeight/1.5f, currentAngle + 90, 1, false);
			}
			
		}

	}
	
	public int getVisibilidadSegunDistancia() {
		if (distance > distanciaMayorTransparencia)
			return visibilidadMinima;
		else if (distance < distanciaSinTransparencia)
			return 255;
		int visibilidad = (int) (Math.round(pendiente * (distance - distanciaSinTransparencia) + 255f));
		return Math.max(visibilidad, visibilidadMinima);
	}
	
	protected int getColorARGB()	{
		int color = poi.getCategoria().getColor();
		return Color.argb(getVisibilidadSegunDistancia(), Color.red(color), Color.green(color), Color.blue(color));

	}
	
	//deprecated
	public boolean fClick(float x, float y, MixContext ctx, MixState state) {
		boolean evtHandled = false;
		
//		System.out.println("click en AR: y=<" + y + "> | correccion = " + y + " - " + VAR.getActionbarSherlockHeight() + " -> " + (y-VAR.getActionbarSherlockHeight()));
		
		if (isClickValid(x, y - VAR.getActionbarSherlockHeight()) && !obstruido) {
//			evtHandled = state.handleEvent(ctx, this);		
			if (VAR.d != null) {
				try {
					VAR.d.dismiss();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
 			VAR.d = new POI_Locked_Dialog(this,VAR.getContext());			
		}		
		else {
			if (this.isUnicoMarker()){
				//AGREGANDO LOCKED DIALOG
				if (VAR.d != null) {
					try {
						VAR.d.dismiss();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				VAR.d = new POI_Locked_Dialog(this,VAR.getContext());	
			}
		}
		return evtHandled;
	}	

	public double getDistance() {
		return distance;
	}

	public void setDistance(double distance) {
		this.distance = distance;
	}
	

	public String getID() {
		return ID;
	}

	public void setID(String iD) {
		ID = iD;
	}

	@Override
	public int compareTo(Marker another) {

		Marker leftPm = this;
		Marker rightPm = another;

		return Double.compare(leftPm.getDistance(), rightPm.getDistance());

	}

	@Override
	public boolean equals (Object marker) {
		return this.ID.equals(((Marker) marker).getID());
	}

	public boolean isActive() {
		return active;
	}

	public void setActive(boolean active) {
		this.active = active;
	}
	
	public boolean isUnicoMarker() {
		return unicoMarker;
	}

	public void setUnicoMarker(boolean unicoMarker) {
		this.unicoMarker = unicoMarker;
	}
	
	public void anadirObstruido(Marker ma) {
		obstruidos.add(ma);
	}
	
	public void clearObstruidos() {
		obstruidos.clear();
	}

	public String distanciaInmediata() {		
		String textStr="";
		double d = distance;
		DecimalFormat df = new DecimalFormat("@#");
		if(d<1000.0) {
			textStr = title + " - ("+ df.format(d) + " m)";			
		}
		else {
			d=d/1000.0;
			textStr = title + " - (" + df.format(d) + " km)";
		}
		return textStr;
	}
	
	public String solo_distancia() {
		String textStr="";
		double d = distance;
		DecimalFormat df = new DecimalFormat("@#");
		if(d<1000.0) {
			textStr =  df.format(d) + " m";			
		}
		else {
			d=d/1000.0;
			textStr = df.format(d) + " km";
		}
		return textStr;
	}
	
	public ArrayList<Marker> getObstruidos() {
		return obstruidos;
	}
	
	
	public Item[] getAcciones() {
		//cargarAcciones();
		//CharSequence[] cs = acciones.toArray(new CharSequence[acciones.size()]);
		acciones = new ArrayList<Item>();
		acciones.add(new Item("Como llegar", getImg(R.drawable.comollegar), R.drawable.comollegar));
		acciones.add(new Item("Puntos no visibles", getImg(R.drawable.superpuesto), R.drawable.superpuesto)); //si está obstruyendo algún punto
		acciones.add(new Item("Compartir", getImg(R.drawable.share), R.drawable.share));
		acciones.add(new Item("Comentarios", getImg(R.drawable.comentarios30), R.drawable.comentarios30));
		return acciones.toArray(new Item[acciones.size()]);
	}
		
	
	//abstract public int getMaxObjects();

//	public CharSequence[] getPuntosSuperpuestos() {
//		// TODO Auto-generated method stub
//		ArrayList<String> superpuestos = new ArrayList<String>();
//		for (Marker ma : obstruidos) {
//			superpuestos.add(ma.distanciaInmediata());
//		}
//		CharSequence[] cs = superpuestos.toArray(new CharSequence[superpuestos.size()]);
//		return cs;
//	}

	public final Item[] getPuntosSuperpuestos() {
		
		ArrayList<Item> superpuestos = new ArrayList<Item>();
		for (Marker ma : obstruidos) {
			superpuestos.add(new Item(ma.distanciaInmediata(), getImg(poi.getIcono()), poi.getIcono()));
		}
		Item[] items = superpuestos.toArray(new Item[superpuestos.size()]);
		
		POI_Locked_Points.obstruidos = copiarObstruidos();
		return items;
	}
	
	private Drawable getImg( int res )
	{
		Drawable img = VInicial.getContext().getResources().getDrawable( res );
		img.setBounds( 0, 0, 48, 48 );
		return img;
	}
	
	private Bitmap getImgWithSize(Drawable dw, int size) {
		return Bitmap.createScaledBitmap(Utiles.drawableToBitmap(dw), size, size, true);
	}
	
	public void abre_dialogo(/*MixContext context, MixState state*/) {					
		/*boolean evtHandled = false;
        evtHandled = state.handleEvent(context, this);*/
		Intent i = new Intent(VInicial.getContext(), VDownloading.class);
		i.putExtra("sigV", "poi");
		i.putExtra("idPOI", "setted");
		GestorConfiguracion.activity.startActivity(i);
	}
	
	public boolean hay_obstruidos() {
		return !obstruidos.isEmpty();
	}
 
	@SuppressWarnings("unchecked")
	public ArrayList<Marker> copiarObstruidos() {
		if (obstruidos != null)
			return (ArrayList<Marker>) obstruidos.clone();
		else
			return null;
	}
	
}